using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;

namespace SpiderMaster.AspNet.Security.CaptchaImage
{
    /// <summary>
    /// For the Creation of the security Image
    /// </summary>
    public class CaptchaImage
    {
        #region Internal Properties

        private String text;
        private Int32 width;
        private Int32 height;
        private String familyName;
        private Bitmap image;

        // For generating random numbers.
        private Random random = new Random();

        #endregion

        #region Public properties (all read-only).

        public string Text
        {
            get
            {
                return this.text;
            }
        }
        public Bitmap Image
        {
            get
            {
                return this.image;
            }
        }
        public Int32 Width
        {
            get
            {
                return this.width;
            }
        }
        public Int32 Height
        {
            get
            {
                return this.height;
            }
        }

        #endregion

        // ====================================================================
        // Initializes a new instance of the CaptchaImage class using the
        // specified text, width and height.
        // ====================================================================
        public CaptchaImage(string s, Int32 width, Int32 height)
        {
            this.text = s;
            this.SetDimensions(width, height);
            this.GenerateImage();
        }

        // ====================================================================
        // Initializes a new instance of the CaptchaImage class using the
        // specified text, width, height and font family.
        // ====================================================================
        public CaptchaImage(String s, Int32 width, Int32 height, String familyName)
        {
            this.text = s;
            this.SetDimensions(width, height);
            this.SetFamilyName(familyName);
            this.GenerateImage();
        }

        // ====================================================================
        // This member overrides Object.Finalize.
        // ====================================================================
        ~CaptchaImage()
        {
            Dispose(false);
        }

        // ====================================================================
        // Releases all resources used by this Object.
        // ====================================================================
        public void Dispose()
        {
            GC.SuppressFinalize(this);
            this.Dispose(true);
        }

        // ====================================================================
        // Custom Dispose method to clean up unmanaged resources.
        // ====================================================================
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
                // Dispose of the bitmap.
                this.image.Dispose();
        }

        // ====================================================================
        // Sets the image width and height.
        // ====================================================================
        private void SetDimensions(Int32 width, Int32 height)
        {
            // Check the width and height.
            if (width <= 0)
                throw new ArgumentOutOfRangeException("width", width, "Argument out of range, must be greater than zero.");
            if (height <= 0)
                throw new ArgumentOutOfRangeException("height", height, "Argument out of range, must be greater than zero.");
            this.width = width;
            this.height = height;
        }

        // ====================================================================
        // Sets the font used for the image text.
        // ====================================================================
        private void SetFamilyName(string familyName)
        {
            // If the named font is not installed, default to a system font.
            try
            {
                Font font = new Font(this.familyName, 50F);
                this.familyName = familyName;
                font.Dispose();
            }
            catch
            {
                this.familyName = FontFamily.GenericSerif.Name;
            }
        }

        // ====================================================================
        // Creates the bitmap image.
        // ====================================================================
        private void GenerateImage()
        {
            // Create a new 32-bit bitmap image.
            Bitmap bitmap = new Bitmap(this.width, this.height, PixelFormat.Format32bppArgb);

            // Create a graphics Object for drawing.
            Graphics g = Graphics.FromImage(bitmap);
            g.SmoothingMode = SmoothingMode.AntiAlias;
            Rectangle rect = new Rectangle(0, 0, this.width, this.height);

            // Set up the text font.
            SizeF size;
            float fontSize = rect.Height + 1;
            Font font;

            // Fill in the background.
            HatchBrush hatchBrush = new HatchBrush(HatchStyle.ZigZag, Color.LightGray, Color.White);
            g.FillRectangle(hatchBrush, rect);
            //hatchBrush = new HatchBrush(HatchStyle.LightHorizontal, Color.LightPink, Color.Transparent);
            //g.FillRectangle(hatchBrush, rect);

            // Set up the text format.
            StringFormat format = new StringFormat();
            format.Alignment = StringAlignment.Near;
            format.LineAlignment = StringAlignment.Near;

            // Adjust the font size until the text fits within the image.
            do
            {
                fontSize--;
                font = new Font(FontFamily.GenericSansSerif.Name, fontSize, FontStyle.Bold);
                size = g.MeasureString(this.text, font, new PointF(0, 0), format);
            } while (size.Width > (rect.Width + 20));

            // Create a path using the text and warp it randomly.
            GraphicsPath path = new GraphicsPath();
            path.AddString(this.text, font.FontFamily, (Int32)font.Style, font.Size, new Point(0, 0), format);
            float v = 4F;
            PointF[] Points =
			{
				new PointF(this.random.Next(rect.Width) / v, this.random.Next(rect.Height) / v),
				new PointF(rect.Width - this.random.Next(rect.Width) / v, this.random.Next(rect.Height) / v),
				new PointF(this.random.Next(rect.Width) / v, rect.Height - this.random.Next(rect.Height) / v),
				new PointF(rect.Width - this.random.Next(rect.Width) / v, rect.Height - this.random.Next(rect.Height) / v)
			};
            Matrix matrix = new Matrix();
            matrix.Translate(0F, 0F);
            path.Warp(Points, rect, matrix, WarpMode.Perspective, 0F);

            // Draw the text.
            hatchBrush = new HatchBrush(HatchStyle.SmallConfetti, Color.Black, Color.Black);
            g.FillPath(hatchBrush, path);

            // Add some random noise.
            Int32 m = Math.Max(rect.Width, rect.Height);
            for (Int32 i = 0; i < (Int32)(rect.Width * rect.Height / 20F); i++)
            {
                Int32 x = this.random.Next(rect.Width);
                Int32 y = this.random.Next(rect.Height);
                Int32 w = this.random.Next(m / 50);
                Int32 h = this.random.Next(m / 50);
                g.FillEllipse(hatchBrush, x, y, w, h);
            }

            // Clean up.
            font.Dispose();
            hatchBrush.Dispose();
            g.Dispose();

            // Set the image.
            this.image = bitmap;
        }
    }
}
